/*******************************
     Create Component Repos
*******************************/

/*
 This will create individual component repositories for each SUI component

  * copy component files from release
  * create commonjs files as index.js for NPM release
  * create release notes that filter only items related to component
  * custom package.json file from template
  * create bower.json from template
  * create README from template
  * create meteor.js file
*/

var gulp = require("gulp"),
  // node dependencies
  console = require("better-console"),
  del = require("del"),
  fs = require("fs"),
  path = require("path"),
  runSequence = require("run-sequence"),
  // admin dependencies
  concatFileNames = require("gulp-concat-filenames"),
  debug = require("gulp-debug"),
  flatten = require("gulp-flatten"),
  git = require("gulp-git"),
  jsonEditor = require("gulp-json-editor"),
  plumber = require("gulp-plumber"),
  rename = require("gulp-rename"),
  replace = require("gulp-replace"),
  tap = require("gulp-tap"),
  util = require("gulp-util"),
  // config
  config = require("../../config/user"),
  release = require("../../config/admin/release"),
  project = require("../../config/project/release"),
  // shorthand
  version = project.version,
  output = config.paths.output;

module.exports = function(callback) {
  var stream,
    index,
    tasks = [];

  for (index in release.components) {
    var component = release.components[index];

    // streams... designed to save time and make coding fun...
    (function(component) {
      var outputDirectory = path.join(release.outputRoot, component),
        isJavascript = fs.existsSync(output.compressed + component + ".js"),
        isCSS = fs.existsSync(output.compressed + component + ".css"),
        capitalizedComponent =
          component.charAt(0).toUpperCase() + component.slice(1),
        packageName = release.packageRoot + component,
        repoName = release.componentRepoRoot + capitalizedComponent,
        gitURL = "https://github.com/" + release.org + "/" + repoName + ".git",
        repoURL = "https://github.com/" + release.org + "/" + repoName + "/",
        concatSettings = {
          newline: "",
          root: outputDirectory,
          prepend: "    '",
          append: "',"
        },
        regExp = {
          match: {
            // templated values
            name: "{component}",
            titleName: "{Component}",
            version: "{version}",
            files: "{files}",
            // release notes
            spacedVersions: /(###.*\n)\n+(?=###)/gm,
            spacedLists: /(^- .*\n)\n+(?=^-)/gm,
            trim: /^\s+|\s+$/g,
            unrelatedNotes: new RegExp(
              "^((?!(^.*(" + component + ").*$|###.*)).)*$",
              "gmi"
            ),
            whitespace: /\n\s*\n\s*\n/gm,
            // npm
            componentExport: /(.*)\$\.fn\.\w+\s*=\s*function\(([^\)]*)\)\s*{/g,
            componentReference: "$.fn." + component,
            settingsExport: /\$\.fn\.\w+\.settings\s*=/g,
            settingsReference: /\$\.fn\.\w+\.settings/g,
            trailingComma: /,(?=[^,]*$)/,
            jQuery: /jQuery/g
          },
          replace: {
            // readme
            name: component,
            titleName: capitalizedComponent,
            // release notes
            spacedVersions: "",
            spacedLists: "$1",
            trim: "",
            unrelatedNotes: "",
            whitespace: "\n\n",
            // npm
            componentExport:
              "var _module = module;\n$1module.exports = function($2) {",
            componentReference: "_module.exports",
            settingsExport: "module.exports.settings =",
            settingsReference: "_module.exports.settings",
            jQuery: 'require("jquery")'
          }
        },
        task = {
          all: component + " creating",
          repo: component + " create repo",
          bower: component + " create bower.json",
          readme: component + " create README",
          npm: component + " create NPM Module",
          notes: component + " create release notes",
          composer: component + " create composer.json",
          package: component + " create package.json",
          meteor: component + " create meteor package.js"
        },
        // paths to includable assets
        manifest = {
          assets: outputDirectory + "/assets/**/" + component + "?(s).*",
          component: outputDirectory + "/" + component + "+(.js|.css)"
        };

      // copy dist files into output folder adjusting asset paths
      gulp.task(task.repo, false, function() {
        return gulp
          .src(release.source + component + ".*")
          .pipe(plumber())
          .pipe(flatten())
          .pipe(replace(release.paths.source, release.paths.output))
          .pipe(gulp.dest(outputDirectory));
      });

      // create npm module
      gulp.task(task.npm, false, function() {
        return gulp
          .src(release.source + component + "!(*.min|*.map).js")
          .pipe(plumber())
          .pipe(flatten())
          .pipe(
            replace(
              regExp.match.componentExport,
              regExp.replace.componentExport
            )
          )
          .pipe(
            replace(
              regExp.match.componentReference,
              regExp.replace.componentReference
            )
          )
          .pipe(
            replace(regExp.match.settingsExport, regExp.replace.settingsExport)
          )
          .pipe(
            replace(
              regExp.match.settingsReference,
              regExp.replace.settingsReference
            )
          )
          .pipe(replace(regExp.match.jQuery, regExp.replace.jQuery))
          .pipe(rename("index.js"))
          .pipe(gulp.dest(outputDirectory));
      });

      // create readme
      gulp.task(task.readme, false, function() {
        return gulp
          .src(release.templates.readme)
          .pipe(plumber())
          .pipe(flatten())
          .pipe(replace(regExp.match.name, regExp.replace.name))
          .pipe(replace(regExp.match.titleName, regExp.replace.titleName))
          .pipe(gulp.dest(outputDirectory));
      });

      // extend bower.json
      gulp.task(task.bower, false, function() {
        return gulp
          .src(release.templates.bower)
          .pipe(plumber())
          .pipe(flatten())
          .pipe(
            jsonEditor(function(bower) {
              bower.name = packageName;
              bower.description = capitalizedComponent + " - Semantic UI";
              if (isJavascript) {
                if (isCSS) {
                  bower.main = [component + ".js", component + ".css"];
                } else {
                  bower.main = [component + ".js"];
                }
                bower.dependencies = {
                  jquery: ">=1.8"
                };
              } else {
                bower.main = [component + ".css"];
              }
              return bower;
            })
          )
          .pipe(gulp.dest(outputDirectory));
      });

      // extend package.json
      gulp.task(task.package, false, function() {
        return gulp
          .src(release.templates.package)
          .pipe(plumber())
          .pipe(flatten())
          .pipe(
            jsonEditor(function(npm) {
              if (isJavascript) {
                npm.dependencies = {
                  jquery: "x.x.x"
                };
                npm.main = "index.js";
              }
              npm.name = packageName;
              if (version) {
                npm.version = version;
              }
              npm.title = "Semantic UI - " + capitalizedComponent;
              npm.description = "Single component release of " + component;
              npm.repository = {
                type: "git",
                url: gitURL
              };
              return npm;
            })
          )
          .pipe(gulp.dest(outputDirectory));
      });

      // extend composer.json
      gulp.task(task.composer, false, function() {
        return gulp
          .src(release.templates.composer)
          .pipe(plumber())
          .pipe(flatten())
          .pipe(
            jsonEditor(function(composer) {
              if (isJavascript) {
                composer.dependencies = {
                  jquery: "x.x.x"
                };
                composer.main = component + ".js";
              }
              composer.name = "semantic/" + component;
              if (version) {
                composer.version = version;
              }
              composer.description = "Single component release of " + component;
              return composer;
            })
          )
          .pipe(gulp.dest(outputDirectory));
      });

      // create release notes
      gulp.task(task.notes, false, function() {
        return (
          gulp
            .src(release.templates.notes)
            .pipe(plumber())
            .pipe(flatten())
            // Remove release notes for lines not mentioning component
            .pipe(
              replace(
                regExp.match.unrelatedNotes,
                regExp.replace.unrelatedNotes
              )
            )
            .pipe(replace(regExp.match.whitespace, regExp.replace.whitespace))
            .pipe(
              replace(
                regExp.match.spacedVersions,
                regExp.replace.spacedVersions
              )
            )
            .pipe(replace(regExp.match.spacedLists, regExp.replace.spacedLists))
            .pipe(replace(regExp.match.trim, regExp.replace.trim))
            .pipe(gulp.dest(outputDirectory))
        );
      });

      // Creates meteor package.js
      gulp.task(task.meteor, function() {
        var filenames = "";
        return gulp
          .src(manifest.component)
          .pipe(concatFileNames("empty.txt", concatSettings))
          .pipe(
            tap(function(file) {
              filenames += file.contents;
            })
          )
          .on("end", function() {
            gulp
              .src(manifest.assets)
              .pipe(concatFileNames("empty.txt", concatSettings))
              .pipe(
                tap(function(file) {
                  filenames += file.contents;
                })
              )
              .on("end", function() {
                // remove trailing slash
                filenames = filenames
                  .replace(regExp.match.trailingComma, "")
                  .trim();
                gulp
                  .src(release.templates.meteor.component)
                  .pipe(plumber())
                  .pipe(flatten())
                  .pipe(replace(regExp.match.name, regExp.replace.name))
                  .pipe(
                    replace(regExp.match.titleName, regExp.replace.titleName)
                  )
                  .pipe(replace(regExp.match.version, version))
                  .pipe(replace(regExp.match.files, filenames))
                  .pipe(rename(release.files.meteor))
                  .pipe(gulp.dest(outputDirectory));
              });
          });
      });

      // synchronous tasks in orchestrator? I think not
      gulp.task(task.all, false, function(callback) {
        runSequence(
          [
            task.repo,
            task.npm,
            task.bower,
            task.readme,
            task.package,
            task.composer,
            task.notes,
            task.meteor
          ],
          callback
        );
      });

      tasks.push(task.all);
    })(component);
  }

  runSequence(tasks, callback);
};
